/*
 *  Copyright (C) 2005-2007 Renier Roth
 *  www.renier.de
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package de.renier.jkeepass.components;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.image.BufferedImage;

import javax.swing.JComponent;

import de.renier.jkeepass.JKeePass;
import de.renier.jkeepass.elements.BasicElement;
import de.renier.jkeepass.elements.Element;
import de.renier.jkeepass.model.BasicElementTableModel;

/**
 * BasicElementTableTransferHandler
 *
 * @author <a href="mailto:software@renier.de">Renier Roth</a>
 */
public class BasicElementTableTransferHandler implements DragGestureListener, DragSourceListener, DropTargetListener {

  private BasicElementTable table;
  private DragSource dragSource; // dragsource
  private DropTarget dropTarget;
  private Element localElement = null;
  private Rectangle rect2D = new Rectangle();
  private int currentDragRow = -1;

  /**
   * Constructor for BasicElementTableTransferHandler
   *
   * @param table
   * @param action
   */
  protected BasicElementTableTransferHandler(BasicElementTable table, int action) {
    this.table = table;
    dragSource = new DragSource();
    dragSource.createDefaultDragGestureRecognizer(table, action, this);
    dropTarget = new DropTarget(table, action, this);
  }

  /* ********  D R A G   S O U R C E   L I S T E N E R   M E T H O D S  ******** */
  /*
   * (non-Javadoc)
   * @see java.awt.dnd.DragSourceListener#dragDropEnd(java.awt.dnd.DragSourceDropEvent)
   */
  public void dragDropEnd(DragSourceDropEvent dsde) {
    if (dsde.getDropSuccess()) {
      try {
        Transferable transferable = dsde.getDragSourceContext().getTransferable();
        BasicElement element = (BasicElement)transferable.getTransferData(TransferableElement.ELEMENT_FLAVOR);
        // if we have Moved the element we had to remove it
        if (dsde.getDropAction() == DnDConstants.ACTION_MOVE && element != null) {
          ((BasicElementTableModel)table.getModel()).removeBasicElement(element);
          // mark modified
          JKeePass.application.setModified(true);
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
    localElement = null;
  }

  /*
   * (non-Javadoc)
   * @see java.awt.dnd.DragSourceListener#dragEnter(java.awt.dnd.DragSourceDragEvent)
   */
  public void dragEnter(DragSourceDragEvent dsde) {
    int action = dsde.getDropAction();
    if (action == DnDConstants.ACTION_COPY) {
      dsde.getDragSourceContext().setCursor(DragSource.DefaultCopyDrop);
    } else {
      if (action == DnDConstants.ACTION_MOVE) {
        dsde.getDragSourceContext().setCursor(DragSource.DefaultMoveDrop);
      } else {
        dsde.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop);
      }
    }
  }

  /*
   * (non-Javadoc)
   * @see java.awt.dnd.DragSourceListener#dragOver(java.awt.dnd.DragSourceDragEvent)
   */
  public void dragOver(DragSourceDragEvent dsde) {
    int action = dsde.getDropAction();
    if (action == DnDConstants.ACTION_COPY) {
      dsde.getDragSourceContext().setCursor(DragSource.DefaultCopyDrop);
    } else {
      if (action == DnDConstants.ACTION_MOVE) {
        dsde.getDragSourceContext().setCursor(DragSource.DefaultMoveDrop);
      } else {
        dsde.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop);
      }
    }
  }

  /*
   * (non-Javadoc)
   * @see java.awt.dnd.DragSourceListener#dropActionChanged(java.awt.dnd.DragSourceDragEvent)
   */
  public void dropActionChanged(DragSourceDragEvent dsde) {
    int action = dsde.getDropAction();
    if (action == DnDConstants.ACTION_COPY) {
      dsde.getDragSourceContext().setCursor(DragSource.DefaultCopyDrop);
    } else {
      if (action == DnDConstants.ACTION_MOVE) {
        dsde.getDragSourceContext().setCursor(DragSource.DefaultMoveDrop);
      } else {
        dsde.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop);
      }
    }
  }

  /*
   * (non-Javadoc)
   * @see java.awt.dnd.DragSourceListener#dragExit(java.awt.dnd.DragSourceEvent)
   */
  public void dragExit(DragSourceEvent dse) {
    dse.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop);
  }

  /* ********  D R A G   G E S T U R E   L I S T E N E R   M E T H O D S  ******** */  
  /*
   * (non-Javadoc)
   * @see java.awt.dnd.DragGestureListener#dragGestureRecognized(java.awt.dnd.DragGestureEvent)
   */
  public void dragGestureRecognized(DragGestureEvent dge) {
    int row = table.getSelectedRow();
    int column = table.getSelectedColumn();
    if (row >= 0) {
      localElement = ((BasicElementTableModel)table.getModel()).getBasicElement(row);
      Transferable transferable = new TransferableElement(localElement);
      BufferedImage image = null;
      if (DragSource.isDragImageSupported()) {
        Rectangle pathBounds = table.getCellRect(row, column, false);
        // returning the label
        JComponent lbl = (JComponent) table.getCellRenderer(row, column).getTableCellRendererComponent(table, localElement, 
                                                                                                      false, false, row, column);
        // setting bounds to lbl
        lbl.setBounds(pathBounds);
        // buffered image the label's ht and width
        image = new BufferedImage(lbl.getWidth(), lbl.getHeight(), java.awt.image.BufferedImage.TYPE_INT_ARGB_PRE);
        // creating the graphics for buffered image
        Graphics2D graphics = image.createGraphics();
        // Sets the Composite for the Graphics2D context
        graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
        // painting the graphics to label
        lbl.paint(graphics); 
        graphics.dispose();
      }
      dragSource.startDrag(dge, DragSource.DefaultMoveNoDrop, image, new Point(0, 0), transferable, this);
    }
  }

  /* ********  D R O P   T A R G E T   L I S T E N E R   M E T H O D S  ******** */
  /*
   * (non-Javadoc)
   * @see java.awt.dnd.DropTargetListener#dragEnter(java.awt.dnd.DropTargetDragEvent)
   */
  public void dragEnter(DropTargetDragEvent dtde) {
    dtde.acceptDrag(dtde.getDropAction());
  }

  /*
   * (non-Javadoc)
   * @see java.awt.dnd.DropTargetListener#dragExit(java.awt.dnd.DropTargetEvent)
   */
  public void dragExit(DropTargetEvent dte) {
    clearHighlight();
  }

  /*
   * (non-Javadoc)
   * @see java.awt.dnd.DropTargetListener#dragOver(java.awt.dnd.DropTargetDragEvent)
   */
  public void dragOver(DropTargetDragEvent dtde) {
    Point pt = dtde.getLocation();
    paintHighlight(pt);
    table.autoscroll(pt);
    if (dtde.getCurrentDataFlavorsAsList().contains(TransferableElement.ELEMENT_FLAVOR)) {
      int newPosition = table.rowAtPoint(pt);
      if (newPosition >= 0) {
        Element targetElement = ((BasicElementTableModel)table.getModel()).getBasicElement(newPosition);
        if (canLocalPerform(targetElement, dtde.getDropAction())) {
          dtde.acceptDrag(dtde.getDropAction());
          return;
        }
      }
    }
    dtde.rejectDrag();    
  }

  /*
   * (non-Javadoc)
   * @see java.awt.dnd.DropTargetListener#dropActionChanged(java.awt.dnd.DropTargetDragEvent)
   */
  public void dropActionChanged(DropTargetDragEvent dtde) {
    dtde.acceptDrag(dtde.getDropAction());
  }

  /*
   * (non-Javadoc)
   * @see java.awt.dnd.DropTargetListener#drop(java.awt.dnd.DropTargetDropEvent)
   */
  public void drop(DropTargetDropEvent dtde) {
    try {
      int action = dtde.getDropAction();
      clearHighlight();
      Transferable transferable = dtde.getTransferable();
      
      if (transferable.isDataFlavorSupported(TransferableElement.ELEMENT_FLAVOR)) {
        Point pt = dtde.getLocation();
        int newPosition = table.rowAtPoint(pt);
        BasicElement targetElement = ((BasicElementTableModel)table.getModel()).getBasicElement(newPosition);
        if (canLocalPerform(targetElement, action)) {
          dtde.acceptDrop(action);
          BasicElement element = (BasicElement)transferable.getTransferData(TransferableElement.ELEMENT_FLAVOR);
          if (executeDrop(element, newPosition)) {
            dtde.dropComplete(true);
            return;
          }
        }
      } else {
        dtde.rejectDrop();
      }
      dtde.dropComplete(false);
    } catch (Exception e) {
      System.out.println(e);
      e.printStackTrace();
      dtde.rejectDrop();
      dtde.dropComplete(false);
    }
  }

  /**
   * canLocalPerform
   *
   * @param targetElement
   * @param action
   * @return
   */
  private boolean canLocalPerform(Element targetElement, int action) {
    // if we move we can not move to itself or decendent, only local
    if (action == DnDConstants.ACTION_MOVE && 
        (localElement != null &&  localElement.equals(targetElement))) {
      return false;
    }
    return true;
  }
  
  /**
   * executeDrop
   *
   * @param target
   * @param element
   * @param newPosition
   * @param action
   * @return
   */
  private boolean executeDrop(BasicElement element, int newPosition) {
    ((BasicElementTableModel)table.getModel()).insertBasicElement(element, newPosition);
    // mark modified
    JKeePass.application.setModified(true);
    return true;
  }
  
  /**
   * paintHighlight
   *
   * @param pt
   */
  private final void paintHighlight(Point pt) {
    int row = table.rowAtPoint(pt);
    if (row != currentDragRow && row >= 0) {
      clearHighlight();
      currentDragRow = row;
      rect2D = table.getCellRect(row, 0, true);
      rect2D.setSize(table.getBounds().width, rect2D.height);
      //tree.getGraphics().drawRect(rect2D.x, rect2D.y, rect2D.width-1, rect2D.height-1);
      BufferedImage image = new BufferedImage(rect2D.width, rect2D.height, java.awt.image.BufferedImage.TYPE_INT_ARGB_PRE);
      Graphics2D graphics = image.createGraphics();
      graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f)); //Sets
      graphics.setColor(Color.BLUE);
      graphics.fillRect(0, 0, rect2D.width, rect2D.height);
      graphics.dispose();
      table.getGraphics().drawImage(image, rect2D.x, rect2D.y, table);
    }
  }
  
  /**
   * clearHighlight
   *
   */
  private final void clearHighlight() {
    if (rect2D != null) table.paintImmediately(rect2D);
    currentDragRow = -1;
  }  
  
}